# -*- coding: utf-8 -*-

import sqlalchemy as sa
from tornado.ioloop import IOLoop

from bopress import options, metas
from bopress import settings
from bopress.cache import Cache
from bopress.forms import FormData, UserModelForm, UserProfileModelForm
from bopress.hook import Hooks, add_action, add_menu_page, add_submenu_page, add_option_page, add_filter, do_action
from bopress.mail import EMailQueue
from bopress.model import Users
from bopress.orm import SessionFactory, pk
from bopress.ui import AbstractListTableDataProvider
from bopress.user import get_super_user_id, registry, get_user_status, get_user_roles
from bopress.user import update as update_user
from bopress.utils import Utils

__author__ = 'yezang'


def bo_options_general_save(handler):
    if handler.auth({'r_manage_options'}):
        opt_name = handler.get_argument("option_name")
        if not opt_name:
            handler.render_json(success=False)
        else:
            params = dict()
            for p in handler.request.arguments.keys():
                if p.startswith("opt_"):
                    k = p.replace("opt_", "")
                    params[k] = handler.get_arguments(p)

            options.save_options(opt_name, params)
            handler.render_json(success=True)


def bo_options_general_get(handler):
    if handler.auth({'r_manage_options'}):
        opt_name = handler.get_argument("option_name")
        if not opt_name:
            handler.render_json(success=False)
        else:
            opt_value = options.get_options(opt_name)
            handler.render_json(success=True, data=opt_value)


def bo_role_caps_load(handler):
    """
    load roles and all caps and groups, then bind to a html element.
    :param handler:
    """
    if handler.auth({'r_list_users'}):
        v = options.get_options("bo_capabilities")
        vv = options.get_options("bo_roles")
        r = dict()
        r["roles"] = sorted(vv.items(), key=lambda d: d[0])
        r["caps"] = sorted(v.items(), key=lambda d: d[0])
        c_arr = list()
        c_set = set()
        for c in v.values():
            c_set.add(c[0])
            c_arr.append(c[0])
        c_map = dict()
        for i in c_set:
            n = c_arr.count(i)
            c_map[i] = n
        r["groups"] = sorted(c_map.items(), key=lambda d: d[0])
        handler.render_json(success=True, data=r)


def bo_role_caps_add(handler):
    """
    add a role
    :param handler: `.handlers.BaseHandler`
    """
    if handler.auth({'r_list_users'}):
        name = handler.get_argument("name")
        display_name = handler.get_argument("display_name")
        copy_name = handler.get_argument("copy_name")
        if name:
            vv = options.get_options("bo_roles")
            if name in vv.keys():
                handler.render_json(success=False, msg="Exists")
            else:
                caps = list()
                if copy_name:
                    caps = [c for c in vv[copy_name][1]]
                vv[name] = [display_name, caps]
                options.save_options("bo_roles", vv)
                handler.render_json(success=True)
        else:
            handler.render_json(success=False, msg="404")


def bo_role_caps_rename(handler):
    """
    rename role display name of current selected role name.
    :param handler: `.handlers.BaseHandler`
    """
    if handler.auth({'r_list_users'}):
        name = handler.get_argument("name")
        display_name = handler.get_argument("display_name")
        if name:
            vv = options.get_options("bo_roles")
            if name not in vv.keys():
                handler.render_json(success=False, msg="No Exists")
            else:
                vv[name][0] = display_name
                options.save_options("bo_roles", vv)
                handler.render_json(success=True)
        else:
            handler.render_json(success=False, msg="404")


def bo_role_caps_save(handler):
    """
    save caps for current selected role name.
    :param handler: `.handlers.BaseHandler`
    """
    if handler.auth({'r_list_users'}):
        name = handler.get_argument("current_edit_role_name")
        caps = handler.get_arguments("bo_role_cap_item")
        if name:
            vv = options.get_options("bo_roles")
            if name not in vv.keys():
                handler.render_json(success=False, msg="No Exists")
            else:
                vv[name][1] += caps
                vv[name][1] = list(set(vv[name][1]))
                options.save_options("bo_roles", vv)
                handler.render_json(success=True)
        else:
            handler.render_json(success=False, msg="404")


def bo_role_caps_clear(handler):
    """
    clear all caps of current selected role name.
    :param handler: `.handlers.BaseHandler`
    """
    if handler.auth({'r_list_users'}):
        name = handler.get_argument("current_edit_role_name")
        if name:
            vv = options.get_options("bo_roles")
            if name not in vv.keys():
                handler.render_json(success=False, msg="No Exists")
            else:
                vv[name][1] = ['r_read']
                options.save_options("bo_roles", vv)
                handler.render_json(success=True)
        else:
            handler.render_json(success=False, msg="404")


def bo_role_cap_delete(handler):
    """
    delete a cap of current selected role name.
    :param handler: `.handlers.BaseHandler`
    """
    if handler.auth({'r_list_users'}):
        name = handler.get_argument("current_edit_role_name")
        if name:
            vv = options.get_options("bo_roles")
            if name not in vv.keys():
                handler.render_json(success=False, msg="No Exists")
            else:
                cap = handler.get_argument("permission")
                if cap in vv[name][1]:
                    vv[name][1].remove(cap)
                    options.save_options("bo_roles", vv)
                handler.render_json(success=True)
        else:
            handler.render_json(success=False, msg="404")


def site_settings_form(form, current_screen):
    handler = current_screen.handler
    if current_screen.id == "bo-site-settings":
        roles = options.get_options("bo_roles")
        roles_html = list()
        roles_html.append('<div class="form-group">')
        roles_html.append('<label for="default_role">新用户默认角色</label>')
        roles_html.append('<select class="form-control option" id="default_role" name="opt_default_role">')
        for role in roles:
            roles_html.append('<option value="' + role + '">' + roles[role][0] + '</option>')
        roles_html.append('</select>')
        roles_html.append('</div>')

        form = """
        <div class="form-group">
          <label for="site_name">站点名称</label>
          <input type="text" class="form-control" id="site_name" name="opt_site_name" placeholder="">
        </div>
        <div class="form-group">
          <label for="site_url">域名</label>
          <input type="text" class="form-control" id="site_url" name="opt_site_url" placeholder="">
        </div>
        <div class="form-group">
          <label for="site_email">管理员邮箱</label>
          <input type="text" class="form-control" id="site_email" name="opt_site_email" placeholder="">
        </div>
        <div class="form-group">
          <label for="users_can_register">任何人都可以注册</label>
          <select class="form-control" id="users_can_register" name="opt_users_can_register">
          <option value="Y">是</option>
          <option value="N">否</option>
          </select>
        </div>
        %s
        <div class="form-group">
          <label for="max_fileupload_size">最大文件上传尺寸(MB)</label>
          <input type="number" class="form-control" id="max_fileupload_size" name="opt_max_fileupload_size" placeholder="" value="100">
        </div>
        """ % "".join(roles_html)
    return form


def smtp_server_settings_form(form, current_screen):
    if current_screen.id == "bo-smtp-settings":
        form = """
        <div class="form-group">
          <label for="smtp_server">服务器</label>
          <input type="text" class="form-control" id="smtp_server" name="opt_smtp_server" placeholder="">
        </div>
        <div class="form-group">
          <label for="smtp_port">端口</label>
          <input type="number" class="form-control" id="smtp_port" name="opt_smtp_port" placeholder="端口">
        </div>
        <div class="form-group">
          <label for="smtp_auth">账号认证</label>
          <select class="form-control" id="smtp_auth" name="opt_smtp_auth">
          <option value="Y">是</option>
          <option value="N">否</option>
          </select>
        </div>
        <div class="form-group">
          <label for="smtp_security">安全连接</label>
          <select class="form-control" id="smtp_security" name="opt_smtp_security">
          <option value="">无</option>
          <option value="ssl">ssl</option>
          <option value="tls">tls</option>
          </select>
        </div>
        <div class="form-group">
          <label for="smtp_user_name">账号</label>
          <input type="text" class="form-control" id="smtp_user_name" name="opt_user_name" placeholder="账号">
        </div>
        <div class="form-group">
          <label for="smtp_user_pass">密码</label>
          <input type="password" class="form-control" id="smtp_user_pass" name="opt_user_pass" placeholder="密码">
        </div>
        <div class="form-group">
          <label for="smtp_from">发件人地址</label>
          <input type="email" class="form-control" id="smtp_from" name="opt_smtp_from" placeholder="发件人地址">
        </div>
        <div class="form-group">
          <label for="smtp_from_name">发件人姓名</label>
          <input type="text" class="form-control" id="smtp_from_name" name="opt_smtp_from_name" placeholder="发件人姓名">
        </div>
        """
    return form


def bo_profile_page(current_screen):
    handler = current_screen.handler
    s = SessionFactory.session()
    u = s.query(Users).filter(Users.user_id == handler.get_current_user()).one_or_none()
    description = metas.get_user_metas(handler.get_current_user(), "bo_description")
    gravatar = metas.get_user_metas(handler.get_current_user(), "bo_gravatar")
    info = u.__json__()
    info["description"] = description
    info["gravatar"] = gravatar
    f = UserProfileModelForm(data=info)
    form_layouts = {
        0: (
            ('user_login',),
            ('user_email',),
            ('user_mobile_phone',),
            ('display_name',),
            ('user_nicename',),
            ('gravatar',),
            ('description',),
            ('new_pass',)
        )
    }
    handler.render("bocore/admin/profile.html", current_screen=current_screen,
                   form=f, form_layouts=form_layouts)


class UsersListTableDataProvider(AbstractListTableDataProvider):
    def primary_key(self, handler):
        return "user_id"

    def do_data_save(self, handler):
        f = UserModelForm(FormData(handler))
        if f.validate():
            user_id = handler.get_argument('user_id')
            # update user
            if user_id:
                if handler.auth({'r_edit_users'}):
                    r = update_user(user_id, f.user_login.data, f.user_email.data,
                                    f.user_mobile_phone.data, f.user_nicename.data,
                                    f.display_name.data, f.user_status.data, f.roles.data)
                    if r.success:
                        handler.render_json({'user_id': user_id}, "200", True)
                    else:
                        handler.render_json(r.data, r.message, r.success)
            else:
                if handler.auth({'r_create_users'}):
                    pwd = Utils.random_str(8)
                    r = registry(f.user_login.data, pwd, f.user_email.data,
                                 f.user_mobile_phone.data, f.user_nicename.data,
                                 f.display_name.data, f.user_status.data, f.roles.data)
                    if r.success:
                        # send email, if user_status eq ``1``, only send password, otherwise append enable user link.
                        if f.user_status.data == 1:
                            title = u"您的帐号密码"
                            body = pwd
                        else:
                            link = "%s%s?ak=%s" % (settings.DOMAIN,
                                                   handler.reverse_url("verify"), r.data.user_activation_key)
                            title = u"新用户激活"
                            body = u"""<html><body>您的密码是:{0}<br/>点此链接激活账户: <a href="{1}">{1}</a></body></html>"""
                            body = body.format(pwd, link)
                        EMailQueue.send([f.user_email.data], title, body)
                        handler.render_json({'user_id': r.data.user_id}, "200", True)
                    else:
                        handler.render_json(r.data, r.message, r.success)
        else:
            handler.render_json(f.errors, "invalid", False)

    def do_actions(self, action_name, handler):
        ids = handler.get_arguments('primary_keys[]')
        if action_name == "delete":
            if ids:
                if handler.auth({'r_delete_users'}):
                    super_id = get_super_user_id()
                    s = SessionFactory.session()
                    num = s.query(Users).filter(Users.user_id.in_(ids)) \
                        .filter(Users.user_id != super_id).delete(False)
                    s.commit()
                    handler.render_json(num, "200", True)
            else:
                handler.render_json("", "404", False)
        elif action_name == "get":
            if handler.auth({'r_list_users'}):
                s = SessionFactory.session()
                u = s.query(Users).get(ids[0])
                data = u.__json__()
                del data['user_activation_key']
                del data['user_pass']
                del data['user_registered']
                data['user_roles'] = list(metas.get_user_metas(u.user_id, "bo_roles"))
                handler.render_json(data, "200", True)
        elif action_name == "enable":
            if handler.auth({'r_edit_users'}):
                s = SessionFactory.session()
                num = s.query(Users).filter(Users.user_id.in_(ids)).update({Users.user_status: 1}, False)
                s.commit()
                handler.render_json(num, "200", True)
        elif action_name == "disable":
            if handler.auth({'r_edit_users'}):
                s = SessionFactory.session()
                num = s.query(Users.user_status).filter(Users.user_id.in_(ids)).update({Users.user_status: 0}, False)
                s.commit()
                handler.render_json(num, "200", True)
        elif action_name == "repwd":
            if handler.auth({'r_edit_users'}):
                pwd = Utils.random_str(8)
                s = SessionFactory.session()
                num = s.query(Users).filter(Users.user_id == ids[0]).update({Users.user_pass: Utils.md5(pwd)}, False)
                s.commit()
                if num > 0:
                    title = u"您的帐号密码"
                    body = pwd
                    email = s.query(Users.user_email).filter(Users.user_id == ids[0]).scalar()
                    if email:
                        EMailQueue.send(email, title, body)
                handler.render_json(num, "200", True)
        else:
            handler.render_json("", "500", False)

    def data(self, handler, params):
        if handler.auth({'r_list_users'}):
            s = SessionFactory.session()
            search_text = params['search']['value']
            super_id = get_super_user_id()
            order_column = self.first_order_column(params)
            q = s.query(Users)
            if search_text:
                search_text = "%{0}%".format(search_text)
                q = q.filter(sa.or_(Users.user_login.like(search_text),
                                    Users.user_email.like(search_text),
                                    Users.user_mobile_phone.like(search_text),
                                    Users.user_nicename.like(search_text),
                                    Users.display_name.like(search_text))) \
                    .filter(Users.user_id != super_id)
            if order_column and order_column[0] != "user_id":
                q = q.order_by(sa.text('{0} {1}'.format(order_column[0], order_column[1])))
            else:
                q = q.order_by(Users.user_registered.desc())
            q = self.paginate(q, params["start"], params["length"])
            count_q = s.query(Users)
            if search_text:
                search_text = "%{0}%".format(search_text)
                count_q = count_q.filter(sa.or_(Users.user_login.like(search_text),
                                                Users.user_email.like(search_text),
                                                Users.user_mobile_phone.like(search_text),
                                                Users.user_nicename.like(search_text),
                                                Users.display_name.like(search_text))) \
                    .filter(Users.user_id != super_id)
            num = count_q.count()
            items = list()
            for r in q.all():
                items.append(r.__json__())
            self.render_json(handler, items, num, num, params)

    def columns(self, handler):
        return ["user_login",
                "display_name",
                "user_email",
                "user_mobile_phone",
                "user_nicename",
                "user_status",
                "user_registered"]

    def column_titles(self, handler):
        return ["用户名", "昵称", "邮件", "手机", "个人空间名称", "状态", "注册时间"]

    def column_render_cb(self):
        return "bo_userslist_col_render"

    def orderable_columns(self, handler):
        return ["user_status", "user_registered"]

    def forms(self, handler):
        return [UserModelForm()]

    def form_titles(self, handler):
        return ["用户"]

    def dropdown_actions(self, handler):
        return [
            ("enable", "激活"),
            ("disable", "禁用"),
            ("delete", "删除")
        ]

    def row_actions(self, handler):
        return {"user_login": [
            ('get', '编辑', {"after_row_action": "bo_userslist_getone_after_row_action"}),
            ("enable", "激活"),
            ("disable", "禁用"),
            ("repwd", "密码重置"),
            ("delete", "删除")
        ]}

    def form_layouts(self, handler):
        layouts = {
            0: (
                ('roles',),
                ('user_login',),
                ('user_email',),
                ('user_mobile_phone',),
                ('user_nicename',),
                ('user_status',),
                ('display_name',),
            )
        }
        return layouts


def bo_users_view(current_screen):
    handler = current_screen.handler
    handler.render("bocore/admin/users.html", current_screen=current_screen, provider=UsersListTableDataProvider)


def bo_profile_save(handler):
    if handler.auth({'r_read'}):
        f = UserProfileModelForm(FormData(handler))
        if f.validate():
            user_status = get_user_status(handler.get_current_user())
            user_roles = get_user_roles(handler.get_current_user())
            r = update_user(handler.get_current_user(), "", f.user_email.data,
                            f.user_mobile_phone.data, f.user_nicename.data, f.display_name.data,
                            user_status, user_roles, f.description.data, f.gravatar.data)
            if r.success:
                if f.new_pass.data:
                    s = SessionFactory.session()
                    pwd = Utils.md5(f.new_pass.data)
                    s.query(Users).filter(Users.user_id == handler.get_current_user()) \
                        .update({Users.user_pass: pwd})
                    s.commit()
                    EMailQueue.send([f.user_email.data], "密码修改", "新密码: %s" % f.new_pass.data, "plain")
                handler.render_json("", "200", True)
            else:
                handler.render_json("", "错误", False)
        else:
            handler.render_json(f.errors, "", False)


# class ThreadPoolExecutorDemo(object):
#     _thread_pool = ThreadPoolExecutor(1)
#
#     @run_on_executor(executor='_thread_pool')
#     def start(self):
#         import time
#         while True:
#             print(1)
#             time.sleep(5)


def init_data():
    IOLoop.current().spawn_callback(EMailQueue.start)
    v = options.get_options("bo_capabilities")
    if not v:
        c = dict()
        # general 组
        c["r_read"] = ("general", "浏览")
        # user 组
        c["r_list_users"] = ("user", "查看用户列表")
        c["r_create_users"] = ("user", "创建用户")
        c["r_edit_users"] = ("user", "编辑用户")
        c["r_delete_users"] = ("user", "删除用户")
        # file 组
        c["r_edit_files"] = ("file", "编辑附件")
        c["r_upload_files"] = ("file", "上传附件")
        # admin 组
        c["r_manage_options"] = ("admin", "管理选项")
        c["r_manage_plugins"] = ("admin", "管理插件")
        options.save_options("bo_capabilities", c)

    vv = options.get_options("bo_roles")
    if not vv:
        c = dict()
        c["subscriber"] = ["订阅者", ["r_read"]]
        options.save_options("bo_roles", c)

    # create super user
    s = SessionFactory.session()
    num = s.query(Users).count()
    if num == 0:
        u = Users()
        u.user_id = pk()
        u.user_login = settings.ADMIN_USER_LOGIN
        u.user_pass = Utils.md5(settings.ADMIN_USER_LOGIN)
        u.user_status = 1
        u.user_email = settings.ADMIN_USER_EMAIL
        u.user_nicename = settings.ADMIN_USER_LOGIN
        u.display_name = settings.ADMIN_USER_LOGIN
        u.user_registered = Utils.current_datetime()
        s.add(u)
        s.commit()
        metas.save_user_metas(u.user_id, "bo_super", True)
        metas.save_user_metas(u.user_id, "bo_roles", {"super"})
        options.save_options("bo_super_user", u.user_id, auto_load="no")

    do_action("bo_init")


def bo_plugins_page(current_screen):
    handler = current_screen.handler
    plugins = Cache.data().get(["bo_plugins"], dict())
    arr = Hooks.plugins()
    for p in arr:
        status = plugins.get(p["path"], False)
        p["status"] = status
        p["id"] = Utils.md5(p["path"])
    handler.render("bocore/admin/plugins.html", current_screen=current_screen, plugins=arr)


def bo_change_plugin_status(handler):
    if handler.auth({'r_manage_plugins'}):
        plugin_id = handler.get_argument('id', "")
        # enable plugin use True or False
        status_ = handler.get_argument('status', "False")
        status = False
        if status_ == "False":
            status = True

        plugins = Cache.data().get(["bo_plugins"], dict())
        for path in plugins:
            k = Utils.md5(path)
            if plugin_id == k:
                plugins[path] = status
                break
        Cache.data().set(["bo_plugins"], plugins, 0)
        handler.render_json()


def bo_data_init():
    site = dict()
    site["site_url"] = [settings.DOMAIN]
    site["site_name"] = ["BoPress"]
    site["site_email"] = [""]
    site["users_can_register"] = ["Y"]
    site["default_role"] = ["subscriber"]
    site["max_fileupload_size"] = ["10"]

    options.init_form_options("bo-site-settings", site)

    smtp = dict()
    smtp["smtp_server"] = [""]
    smtp["smtp_port"] = ["25"]
    smtp["smtp_auth"] = ["Y"]
    smtp["smtp_security"] = [""]
    smtp["smtp_user_name"] = [""]
    smtp["smtp_user_pass"] = [""]
    smtp["smtp_from"] = [""]
    smtp["smtp_from_name"] = ["BoPress"]

    options.init_form_options("bo-smtp-settings", smtp)


def load():
    add_action("bo_init", bo_data_init)
    add_menu_page("仪表盘", "仪表盘", "bo-dashboard", ["r_read"], "bocore/admin/dashboard.html", "", 0)
    add_menu_page("用户管理", "用户", "bo-users", ["r_list_users"], "bocore/admin/blank.html", "", 997)
    add_menu_page("插件管理", "插件", "bo-plugins", ["r_manage_plugins"], bo_plugins_page, "", 998)
    add_menu_page("系统设置", "设置", "bo-options-general", ["r_manage_options"],
                  "bocore/admin/options-general.html", "", 999)
    add_submenu_page("bo-users", "用户管理", "所有用户", "bo-users-list", ["r_list_users"], bo_users_view)
    add_submenu_page("bo-users", "角色权限", "角色权限", "bo-role-capabilities", ["r_list_users"],
                     "bocore/admin/user_role_capabilities.html")
    add_menu_page("个人中心", "个人中心", "bo-profile", ["r_read"], bo_profile_page, "", 1000, place='single')
    add_option_page("通用设置", "通用", "bo-site-settings", ["r_manage_options"])
    add_option_page("SMTP 邮件", "SMTP 邮件", "bo-smtp-settings", ["r_manage_options"])
    add_filter("bo_options_general_form", site_settings_form)
    add_filter("bo_options_general_form", smtp_server_settings_form)
    add_action("bo_api_post_bo_options_general_save", bo_options_general_save)
    add_action("bo_api_post_bo_options_general_get", bo_options_general_get)
    add_action("bo_api_post_bo_role_caps_load", bo_role_caps_load)
    add_action("bo_api_post_bo_role_caps_add", bo_role_caps_add)
    add_action("bo_api_post_bo_role_caps_rename", bo_role_caps_rename)
    add_action("bo_api_post_bo_role_caps_save", bo_role_caps_save)
    add_action("bo_api_post_bo_role_caps_clear", bo_role_caps_clear)
    add_action("bo_api_post_bo_role_cap_delete", bo_role_cap_delete)
    add_action("bo_api_post_bo_profile_save", bo_profile_save)
    add_action("bo_api_post_bo_change_plugin_status", bo_change_plugin_status)
